import { IElement } from "./IElement.js";
import { ITextCharacter } from "./IText.js";
import { ICursor } from "./ICursor.js";

const ACTION_INSERT = Symbol("insert");
const ACTION_DELETE = Symbol("delete");
const MAX_UNDO_REDO_ACTION_SIZE = 100;
const RETURN_CHARACTER = "\n".charCodeAt(0);

export class IInput extends IElement {
    constructor() {
        super();
        this.SetTagName("IInput");
        this.SetLineHeight(this.GetThemeLineHeight());
        this.SetFontSize(this.GetThemeFontSize());
        this.SetPadding(this.GetThemePaddingTopBottom(), this.GetThemePaddingLeftRight());
        this.SetBorder(this.GetThemeBorderWidth(), this.GetThemeBorderStyle(), this.GetThemeBorderColor());
        this.SetBackgroundColor(this.GetThemeBackgroundColor());

        this.ClearSelected();
        this.m_cursor = new ICursor();
        this.m_cursor_index = 0; // 光标索引 文本插入
        this.m_selected_start_index = -1; // 选中起始索引 文本删除
        this.m_selected_end_index = -1; // 选中结束索引
        this.m_text = ""; // 文本
        this.m_characters = []; // 字符列表
        this.m_placeholder = ""; // 占位符
        this.m_undo = []; // 撤销
        this.m_redo = []; // 恢复
        this.m_selected_background_color = "#3390ffff";
        this.m_input_type = "text";
        this.SetAttributeSetterGetter("value", (value) => { this.SetValue(value); });
        this.SetAttributeSetterGetter("placeholder", (placeholder) => { this.SetPlaceholder(placeholder); });
        this.SetAttributeSetterGetter("type", (type) => { this.SetInputType(type); });
    }

    GetCursor() { return this.m_cursor; }
    GetTextLines() { return this.m_textlines; }
    GetCharacters() { return this.m_characters; }
    GetSelectedStartIndex() { return this.m_selected_start_index < this.m_selected_end_index ? this.m_selected_start_index : this.m_selected_end_index; }
    GetSelectedEndIndex() { return this.m_selected_start_index > this.m_selected_end_index ? this.m_selected_start_index : this.m_selected_end_index; }
    GetSelectedSize() { return this.m_selected_start_index >= this.m_selected_end_index ? (this.m_selected_start_index - this.m_selected_end_index) : (this.m_selected_end_index - this.m_selected_start_index); }
    GetSelectedText() { return this.m_text.substring(this.GetSelectedStartIndex(), this.GetSelectedEndIndex()); }
    GetCursorIndex() { return this.m_cursor_index; }
    IsSelected() { return this.m_selected_start_index != this.m_selected_end_index; }
    GetSelectedBackgroundColor() { return this.m_selected_background_color; }
    SetSelectedBackgroundColor(color) { this.m_selected_background_color = color; }
    SetPlaceholder(placeholder) { this.m_placeholder = placeholder; }
    SetInputType(input_type) { this.m_input_type = input_type; }
    GetInputType() { return this.m_input_type; }
    IsPasswordType() { return this.m_input_type === "password"; }
    SetFocus(focus) { this.m_focus = focus; }
    IsFocus() { return this.m_focus; }
    IsEmpty() { return this.m_text.length === 0; }

    SetValue(value) {
        const text = this.GetSupportText(value);
        if (this.m_text === text) return;
        this.m_text = text;
        this.m_characters = this.GetCharactersByText(text);
        this.MarkLayoutChanged();
        this.Refresh();
    }

    OnRenderPlaceholderText(painter) {
        const characters = this.GetCharactersByText(this.m_placeholder);
        const characters_size = characters.length;
        const background_color = this.GetBackgroundColor();
        const font_color = this.GetThemePlaceholderTextColor();
        const font_size = this.GetFontSize();
        const line_height = this.GetLineHeight();
        const font_family = this.GetFontFamily();
        const x = this.GetContentX();
        const y = this.GetContentY();
        const content_viewport_x = this.GetContentViewPortX();
        const content_viewport_y = this.GetContentViewPortY();
        const content_viewport_width = this.GetContentViewPortWidth();
        const content_viewport_height = this.GetContentViewPortHeight();
        let textline_width = 0;
        let textline_height = 0;
        for (let i = 0; i < characters_size; i++) {
            const character = characters[i];
            character.SetFontFamily(font_family);
            character.SetFontSize(font_size);
            character.SetLineHeight(line_height);
            character.UpdateLayout();
            character.SetLocalX(textline_width);
            character.SetLocalY(textline_height);
            character.SetFontColor(font_color);
            character.SetBackgroundColor(background_color);
            textline_width = textline_width + character.GetLocalWidth();
            if (character.GetCharacter() === RETURN_CHARACTER) {
                textline_width = 0;
                textline_height = textline_height + font_size * line_height;
            }
            const character_x = x + character.GetLocalX();
            const character_y = y + character.GetLocalY();
            const character_width = character.GetLocalWidth();
            const character_height = character.GetLocalHeight();
            const character_viewport_x = content_viewport_x < character_x ? character_x : content_viewport_x;
            const character_viewport_y = content_viewport_y < character_y ? character_y : content_viewport_y;
            const character_viewport_width = ((character_x + character_width) > (content_viewport_x + content_viewport_width) ? (content_viewport_x + content_viewport_width) : (character_x + character_width)) - character_viewport_x;
            const character_viewport_height = ((character_y + character_height) > (content_viewport_y + content_viewport_height) ? (content_viewport_y + content_viewport_height) : (character_y + character_height)) - character_viewport_y;
            character.SetX(character_x);
            character.SetY(character_y);
            character.SetWidth(character_width);
            character.SetHeight(character_height);
            character.SetViewPortX(character_viewport_x);
            character.SetViewPortY(character_viewport_y);
            character.SetViewPortWidth(character_viewport_width);
            character.SetViewPortHeight(character_viewport_height);
            character.SetPositionChanged(false);
            character.Render(painter);
        }
    }

    OnRenderContent(painter) {
        if (!this.IsFocus() && this.IsEmpty()) {
            this.OnRenderPlaceholderText(painter);
            return;
        }
        const characters = this.GetCharacters();
        const characters_size = characters.length;
        const x = this.GetX() - this.GetScrollX();
        const y = this.GetY() - this.GetScrollY();
        const content_viewport_x = this.GetContentViewPortX();
        const content_viewport_y = this.GetContentViewPortY();
        const content_viewport_width = this.GetContentViewPortWidth();
        const content_viewport_height = this.GetContentViewPortHeight();
        const selected_start_index = this.GetSelectedStartIndex();
        const selected_end_index = this.GetSelectedEndIndex();
        const background_color = this.GetBackgroundColor();
        const selected_background_color = this.GetSelectedBackgroundColor();
        for (let i = 0; i < characters_size; i++) {
            const character = characters[i];
            const character_x = x + character.GetLocalX();
            const character_y = y + character.GetLocalY();
            const character_width = character.GetLocalWidth();
            const character_height = character.GetLocalHeight();
            const character_viewport_x = content_viewport_x < character_x ? character_x : content_viewport_x;
            const character_viewport_y = content_viewport_y < character_y ? character_y : content_viewport_y;
            const character_viewport_width = ((character_x + character_width) > (content_viewport_x + content_viewport_width) ? (content_viewport_x + content_viewport_width) : (character_x + character_width)) - character_viewport_x;
            const character_viewport_height = ((character_y + character_height) > (content_viewport_y + content_viewport_height) ? (content_viewport_y + content_viewport_height) : (character_y + character_height)) - character_viewport_y;
            character.SetX(character_x);
            character.SetY(character_y);
            character.SetViewPortX(character_viewport_x);
            character.SetViewPortY(character_viewport_y);
            character.SetViewPortWidth(character_viewport_width);
            character.SetViewPortHeight(character_viewport_height);
            if (selected_start_index <= i && i < selected_end_index) {
                character.SetBackgroundColor(selected_background_color);
            } else {
                character.SetBackgroundColor(background_color);
            }
            character.Render(painter);
        }

        const cursor = this.GetCursor();
        cursor.SetVisible(false); // 设为隐藏下次渲染为显示
        cursor.SetFlickerTimeStamp(0);
        cursor.SetViewPortX(content_viewport_x);
        cursor.SetViewPortY(content_viewport_y);
        cursor.SetViewPortWidth(content_viewport_width);
        cursor.SetViewPortHeight(content_viewport_height);
        cursor.Render(painter);
    }

    UpdatePosition() {
        super.UpdatePosition();

        const line_height = this.GetLineHeight() * this.GetFontSize();
        const max_content_width = this.GetMaxContentWidth();
        const max_content_height = this.GetMaxContentHeight();
        const local_content_width = this.GetLocalContentWidth();
        const local_content_height = this.GetLocalContentHeight();

        // 光标局部位置
        const cursor_line_index = this.GetLineIndexByCharIndex(this.m_cursor_index);
        const cursor_textline = this.m_textlines[cursor_line_index];
        const cursor_x = cursor_textline.m_character_widths[this.m_cursor_index - cursor_textline.m_character_index - 1] || 0;
        const cursor_y = line_height * cursor_line_index;

        // 更新滚动条位置
        const max_scroll_x = local_content_width < max_content_width ? 0 : (local_content_width - max_content_width);
        const max_srcoll_y = local_content_height < max_content_height ? 0 : (local_content_height - max_content_height);

        // 矫正滚动条
        let scroll_x = this.GetScrollX();
        let scroll_y = this.GetScrollY();
        scroll_x = scroll_x < 0 ? 0 : (scroll_x >= max_scroll_x ? max_scroll_x : scroll_x);
        scroll_y = scroll_y < 0 ? 0 : (scroll_y >= max_srcoll_y ? max_srcoll_y : scroll_y);

        const max_view_x = max_content_width;
        const max_view_y = max_content_height - line_height;
        const view_x = cursor_x - scroll_x;
        const view_y = cursor_y - scroll_y;

        if (view_x > max_view_x) {
            scroll_x += view_x - max_view_x;
        }
        else if (view_x < 0) {
            scroll_x += view_x;
        }

        if (view_y > max_view_y) {
            scroll_y += view_y - max_view_y;
        }
        else if (view_y < 0) {
            scroll_y += view_y;
        }

        // 设置滚动条
        this.SetScrollX(scroll_x);
        this.SetScrollY(scroll_y);

        // 设置光标
        const cursor = this.GetCursor();
        cursor.SetWidth(1);
        cursor.SetHeight(line_height - 2);
        cursor.SetX(this.GetContentX() + cursor_x - scroll_x);
        cursor.SetY(this.GetContentY() + cursor_y - scroll_y + (line_height - cursor.GetHeight()) / 2);
        cursor.SetViewPortX(this.GetViewPortX());
        cursor.SetViewPortY(this.GetViewPortY());
        cursor.SetViewPortWidth(this.GetViewPortWidth());
        cursor.SetViewPortHeight(this.GetViewPortHeight());
    }

    UpdateLayout() {
        this.m_childrens = [];
        this.ApplyLayoutStyle();
        this.SetLocalWidth(this.GetLayoutWidth());
        this.SetLocalHeight(this.GetLayoutHeight());
        this.SetLayoutChanged(false);
        this.SetPositionChanged(true);

        const textlines = [];
        const font_size = this.GetFontSize();
        const line_height = this.GetLineHeight();
        const font_family = this.GetFontFamily();
        const offset_x = this.GetLeftPaddingBorderSize();
        const offset_y = this.GetTopPaddingBorderSize();
        const characters = this.GetCharacters();
        const characters_size = characters.length;
        const is_password = this.IsPasswordType();
        let character_index = 0;
        let character_size = 0;
        let textline_width = 0;
        let textline_height = 0;
        let max_textline_width = 0;
        let textline = {};
        let character_widths = [];
        for (let i = 0; i < characters_size; i++) {
            const character = characters[i];
            if (is_password) character.SetCharacter('*');
            character.SetFontFamily(font_family);
            character.SetFontSize(font_size);
            character.SetLineHeight(line_height);
            character.UpdateLayout();
            character.SetLocalX(offset_x + textline_width);
            character.SetLocalY(offset_y + textline_height);
            character_size = character_size + 1;
            textline_width = textline_width + character.GetLocalWidth();
            character_widths.push(textline_width);
            max_textline_width = max_textline_width > textline_width ? max_textline_width : textline_width;
            if (character.GetCharacter() === RETURN_CHARACTER) {
                textline.m_line_index = textlines.length; // 行索引
                textline.m_character_index = character_index; // 行字符索引
                textline.m_character_size = character_size; // 行字符数量
                textline.m_character_widths = character_widths;// 行字符宽度
                textlines.push(textline);  // 追加行
                textline_height = textline_height + font_size * line_height;
                textline = {};
                character_widths = [];
                character_index = character_index + character_size;
                character_size = 0;
                textline_width = 0;
            }
        }
        textline.m_line_index = textlines.length
        textline.m_character_index = character_index;
        textline.m_character_size = character_size;
        textline.m_character_widths = character_widths;
        textlines.push(textline);
        textline_height = textline_height + font_size * line_height;

        this.m_textlines = textlines;
        this.m_cursor_index = this.m_cursor_index < 0 ? 0 : (this.m_cursor_index > characters_size ? characters_size : this.m_cursor_index);
        this.SetLocalContentWidth(max_textline_width);
        this.SetLocalContentHeight(textline_height);
    }

    GetLineIndexByCharIndex(char_index) {
        const textlines = this.GetTextLines();
        let start_line_index = 0;
        let end_line_index = textlines.length - 1;
        let line_index = Math.floor((start_line_index + end_line_index) / 2);
        while (start_line_index < end_line_index) {
            const textline = textlines[line_index];
            const min_index = textline.m_character_index;
            const max_index = min_index + textline.m_character_size;
            if (char_index < min_index) {
                end_line_index = line_index - 1;
            }
            else if (char_index >= max_index) {
                start_line_index = line_index + 1;
            }
            else {
                break;
            }
            line_index = Math.floor((start_line_index + end_line_index) / 2);
        }
        return line_index;
    }

    DeleteNextChar() {
        this.SelectedNextChar();
        this.DeleteSelected();
    }

    DeletePrevChar() {
        this.SelectedPrevChar();
        this.DeleteSelected();
    }

    OnKeyEnter() {
        this.InsertText("\n");
    }

    OnKeyTab() {
        this.InsertText("\t");
    }

    OnKeyBackSpace() {
        this.DeletePrevChar();
    }

    OnKeyDelete() {
        this.DeleteNextChar();
    }

    OnKeyArrowLeft() {
        if (this.IsSelected()) {
            this.m_cursor_index = this.GetSelectedStartIndex();
            this.SetPositionChanged(true);
        } else {
            this.GoToPrevChar();
        }
        this.ClearSelected();
    }

    OnKeyArrowRight() {
        if (this.IsSelected()) {
            this.m_cursor_index = this.GetSelectedEndIndex();
            this.SetPositionChanged(true);
        } else {
            this.GoToNextChar();
        }
        this.ClearSelected();
    }

    OnKeyArrowUp() {
        this.GoToPrevLine();
        this.ClearSelected();
    }

    OnKeyArrowDown() {
        this.GoToNextLine();
        this.ClearSelected();
    }

    OnKeyHome() {
        this.GoToHead();
        this.ClearSelected();
    }

    OnKeyEnd() {
        this.GoToTail();
        this.ClearSelected();
    }

    OnKeyPageUp() {
        this.GoToLineHead();
        this.ClearSelected();
    }

    OnKeyPageDown() {
        this.GoToLineTail();
        this.ClearSelected();
    }

    OnKeyShiftArrowLeft() {
        this.SelectedPrevChar();
    }

    OnKeyShiftArrowRight() {
        this.SelectedNextChar();
    }

    OnKeyShiftArrowUp() {
        this.SelectedPrevLine();
    }

    OnKeyShiftArrowDown() {
        this.SelectedNextLine();
    }

    OnKeyShiftHome() {
        this.SelectedHead();
    }

    OnKeyShiftEnd() {
        this.SelectedTail();
    }

    OnKeyShiftPageUp() {
        this.SelectedLineHead();
    }

    OnKeyShiftPageDown() {
        this.SelectedLineTail();
    }

    async OnKeyCtrlC() {
        const selected_text = this.GetSelectedText();
        if (selected_text) {
            await this.GetWindow().SetClipboardText(selected_text);
        }
    }

    async OnKeyCtrlV() {
        const clipboard_text = await this.GetWindow().GetClipboardText();
        if (clipboard_text) {
            this.InsertText(clipboard_text);
        }
    }

    async OnKeyCtrlX() {
        const selected_text = this.GetSelectedText();
        if (selected_text) {
            await this.GetWindow().SetClipboardText(selected_text);
            this.DeleteSelected();
        }
    }

    OnKeyCtrlZ() {
        this.ClearSelected();
        this.PopUndoAction();
    }

    OnKeyCtrlY() {
        this.ClearSelected();
        this.PopRedoAction();
    }

    OnKeyCtrlS() {
    }

    OnKeyCtrlA() {
        this.SelectedAll();
    }

    OnFocus() {
        this.SetFocus(true);
        this.Refresh();
        const window = this.GetWindow();
        const cursor = this.GetCursor();
        cursor.SetFlicker(true);
        window.SetCursor(cursor);
        window.StartTextInput();
    }

    OnBlur() {
        this.SetFocus(false);
        this.Refresh();
        const window = this.GetWindow();
        const cursor = this.GetCursor();
        cursor.SetFlicker(false);
        window.SetCursor(null);
        window.StopTextInput();
    }

    SetCursorXY(x, y) {
        const local_x = Math.max(0, x + this.GetScrollX() - this.GetContentX());
        const local_y = Math.max(0, y + this.GetScrollY() - this.GetContentY());
        const line_height = this.GetLineHeight() * this.GetFontSize();
        const line_index = Math.floor(local_y / line_height);
        const textline = line_index >= this.m_textlines.length ? this.m_textlines[this.m_textlines.length - 1] : this.m_textlines[line_index];
        const character_size = textline.m_character_size;
        const character_index = textline.m_character_index;

        this.m_cursor_index = character_index + character_size;
        let total_character_width = 0;
        for (let i = 0; i < character_size; i++) {
            const index = character_index + i;
            const character = this.m_characters[index];
            const character_width = character.GetWidth();
            const half_character_width = Math.floor(character_width / 2);
            total_character_width = total_character_width + half_character_width;
            if (total_character_width >= local_x) {
                this.m_cursor_index = index;
                break;
            }
            total_character_width = total_character_width - half_character_width + character_width;
        }
    }

    OnMouseDown(event) {
        this.ClearSelected();
        this.SetCursorXY(event.GetX(), event.GetY())
        this.BeginSelected();
        this.CaptureMouse();
        this.SetPositionChanged(true);
        this.Refresh();
    }

    OnMouseMove(event) {
        if (this.GetSelectedStartIndex() >= 0 && event.IsLeftButtonPressed()) {
            this.SetCursorXY(event.GetX(), event.GetY())
            this.EndSelected();
            this.SetPositionChanged(true);
            this.Refresh();
        }
    }

    OnMouseUp(event) {
        if (!this.IsSelected()) this.ClearSelected();
        this.ReleaseMouse();
    }

    OnKeyDown(event) {
        if (event.IsEnter()) {
            this.OnKeyEnter();
        } else if (event.IsDelete()) {
            this.OnKeyDelete();
        } else if (event.IsBackSpace()) {
            this.OnKeyBackSpace();
        } else if (event.IsTab()) {
            this.OnKeyTab();
        } else if (event.IsCtrlC()) {
            this.OnKeyCtrlC();
        } else if (event.IsCtrlX()) {
            this.OnKeyCtrlX();
        } else if (event.IsCtrlV()) {
            this.OnKeyCtrlV();
        } else if (event.IsCtrlZ()) {
            this.OnKeyCtrlZ();
        } else if (event.IsCtrlY()) {
            this.OnKeyCtrlY();
        } else if (event.IsCtrlS()) {
            this.OnKeyCtrlS();
        } else if (event.IsCtrlA()) {
            this.OnKeyCtrlA();
        } else if (event.IsShiftUp()) {
            this.OnKeyShiftArrowUp();
        } else if (event.IsShiftDown()) {
            this.OnKeyShiftArrowDown();
        } else if (event.IsShiftLeft()) {
            this.OnKeyShiftArrowLeft();
        } else if (event.IsShiftRight()) {
            this.OnKeyShiftArrowRight();
        } else if (event.IsShiftHome()) {
            this.OnKeyShiftHome();
        } else if (event.IsShiftEnd()) {
            this.OnKeyShiftEnd();
        } else if (event.IsShiftPageUp()) {
            this.OnKeyShiftPageUp();
        } else if (event.IsShiftPageDown()) {
            this.OnKeyShiftPageDown();
        } else if (event.IsUp()) {
            this.OnKeyArrowUp();
        } else if (event.IsDown()) {
            this.OnKeyArrowDown();
        } else if (event.IsLeft()) {
            this.OnKeyArrowLeft();
        } else if (event.IsRight()) {
            this.OnKeyArrowRight();
        } else if (event.IsHome()) {
            this.OnKeyHome();
        } else if (event.IsEnd()) {
            this.OnKeyEnd();
        } else if (event.IsPageUp()) {
            this.OnKeyPageUp();
        } else if (event.IsPageDown()) {
            this.OnKeyPageDown();
        }

        this.Refresh();
    }

    OnTextInput(text) {
        this.InsertText(text);
    }

    IsSupportCharacter(input_type, character) {
        if (input_type === "integer" || input_type === "positive-integer") {
            if (input_type === "integer" && character === "-") {
                return this.m_text.length === 0 || (this.m_text[0] !== "-" && this.m_cursor_index === 0);
            }
            return "0" <= character && character <= "9";
        } else if (input_type === "number" || input_type === "positive-number") {
            if (input_type === "number" && character === "-") {
                return this.m_text.length === 0 || (this.m_text[0] !== "-" && this.m_cursor_index === 0);
            }
            if (character === "." && this.m_text.indexOf(".") === -1) return true;
            return "0" <= character && character <= "9";
        } else if (input_type === "color") {
            if (character === "#") return this.m_text.length === 0;
            return (character >= "0" && character <= "9") || (character >= "a" && character <= "f") || (character >= "A" && character <= "F");
        } else {
            return true;
        }
    }

    GetSupportText(text) {
        const input_type = this.GetInputType();
        let support_text = "";
        for (let i = 0; i < text.length; i++) {
            const character = text[i];
            if (this.IsSupportCharacter(input_type, character)) {
                support_text += character;
            }
        }
        return support_text;
    }

    GetCharactersByText(text) {
        const characters = [];
        const window = this.GetWindow();
        const root = window.GetRoot();
        const parent = this;
        for (let i = 0; i < text.length; i++) {
            const character = new ITextCharacter();
            character.SetCharacter(text[i]);
            character.SetWindow(window);
            character.SetRoot(root);
            character.SetParent(parent);
            character.SetSupportStyle(false);
            character.SetLayoutChanged(true);
            characters.push(character);
        }
        return characters;
    }

    InsertText(text) {
        const insert_text = this.GetSupportText(text);
        if (insert_text.length === 0 || text.length === 0) return;
        // 删除选中文本
        this.DeleteSelected();

        // 添加撤销操作
        this.PushUndoAction({
            m_action: ACTION_INSERT,
            m_start_index: this.m_cursor_index,
            m_end_index: this.m_cursor_index + insert_text.length,
            m_text: insert_text,
        });

        // 添加字符
        const insert_characters = this.GetCharactersByText(insert_text);
        this.m_text = this.m_text.slice(0, this.m_cursor_index) + insert_text + this.m_text.slice(this.m_cursor_index);
        this.m_characters = this.m_characters.slice(0, this.m_cursor_index).concat(insert_characters).concat(this.m_characters.slice(this.m_cursor_index));

        // 更新光标位置
        this.m_cursor_index = this.m_cursor_index + insert_text.length;

        // 值更新回调
        this.OnChange();
    }

    DeleteSelected() {
        if (!this.IsSelected()) return;
        // 获取选择文本的开始和结束索引
        const selected_start_index = this.GetSelectedStartIndex();
        const selected_end_index = this.GetSelectedEndIndex();

        // 添加撤销操作
        this.PushUndoAction({
            m_action: ACTION_DELETE,
            m_start_index: selected_start_index,
            m_end_index: selected_end_index,
            m_text: this.GetSelectedText(),
        });

        // 删除字符
        this.m_text = this.m_text.slice(0, selected_start_index) + this.m_text.slice(selected_end_index);
        this.m_characters = this.m_characters.slice(0, selected_start_index).concat(this.m_characters.slice(selected_end_index));

        // 更新光标位置
        this.m_cursor_index = selected_start_index;

        // 清空选择
        this.ClearSelected();

        // 值更新回调
        this.OnChange();
    }

    OnChange() {
        this.SetLayoutChanged(true);
        this.Refresh();
    }

    GoToNextChar() {
        this.m_cursor_index = this.m_cursor_index + 1;
        this.m_cursor_index = this.m_cursor_index > this.m_text.length ? this.m_text.length : this.m_cursor_index;
        this.SetPositionChanged(true);
    }

    GoToPrevChar() {
        this.m_cursor_index = this.m_cursor_index - 1;
        this.m_cursor_index = this.m_cursor_index < 0 ? 0 : this.m_cursor_index;
        this.SetPositionChanged(true);
    }

    GoToNextLine() {
        const cursor_line_index = this.GetLineIndexByCharIndex(this.m_cursor_index);
        const cursor_textline = this.m_textlines[cursor_line_index];
        if ((cursor_line_index + 1) >= this.m_textlines.length) {
            this.m_cursor_index = cursor_textline.m_character_index + cursor_textline.m_character_size;
        }
        else {
            const next_textline = this.m_textlines[cursor_line_index + 1];
            const line_offset = (this.m_cursor_index - cursor_textline.m_character_index);
            const max_offset = next_textline.m_character_size - ((next_textline.m_line_index + 1) < this.m_textlines.length ? 1 : 0);
            this.m_cursor_index = next_textline.m_character_index + (line_offset > max_offset ? max_offset : line_offset);
        }
        this.SetPositionChanged(true);
    }

    GoToPrevLine() {
        const cursor_line_index = this.GetLineIndexByCharIndex(this.m_cursor_index);
        const cursor_textline = this.m_textlines[cursor_line_index];
        if (cursor_line_index <= 0) {
            this.m_cursor_index = cursor_textline.m_character_index;
        }
        else {
            const prev_textline = this.m_textlines[cursor_line_index - 1];
            const line_offset = (this.m_cursor_index - cursor_textline.m_character_index);
            const max_offset = prev_textline.m_character_size - ((prev_textline.m_line_index + 1) < this.m_textlines.length ? 1 : 0);
            this.m_cursor_index = prev_textline.m_character_index + (line_offset > max_offset ? max_offset : line_offset);
        }
        this.SetPositionChanged(true);
    }

    GoToLineTail() {
        const cursor_line_index = this.GetLineIndexByCharIndex(this.m_cursor_index);
        const cursor_textline = this.m_textlines[cursor_line_index];
        if ((cursor_line_index + 1) >= this.m_textlines.length) {
            this.m_cursor_index = cursor_textline.m_character_index + cursor_textline.m_character_size;
        }
        else {
            this.m_cursor_index = cursor_textline.m_character_index + cursor_textline.m_character_size - 1;
        }
        this.SetPositionChanged(true);
    }

    GoToLineHead() {
        const cursor_line_index = this.GetLineIndexByCharIndex(this.m_cursor_index);
        const cursor_textline = this.m_textlines[cursor_line_index];
        this.m_cursor_index = cursor_textline.m_character_index;
        this.SetPositionChanged(true);
    }

    GoToTail() {
        this.m_cursor_index = this.m_characters.length;
        this.SetPositionChanged(true);
    }

    GoToHead() {
        this.m_cursor_index = 0;
        this.SetPositionChanged(true);
    }

    ClearSelected() {
        this.m_selected_start_index = -1;
        this.m_selected_end_index = -1;
    }

    BeginSelected() {
        if (this.m_selected_start_index == this.m_selected_end_index) {
            this.m_selected_start_index = this.m_cursor_index;
            this.m_selected_end_index = this.m_cursor_index;
        }
    }

    EndSelected() {
        this.m_selected_end_index = this.m_cursor_index;
    }

    SelectedNextChar() {
        this.BeginSelected();
        this.GoToNextChar();
        this.EndSelected();
    }

    SelectedPrevChar() {
        this.BeginSelected();
        this.GoToPrevChar();
        this.EndSelected();
    }

    SelectedNextLine() {
        this.BeginSelected();
        this.GoToNextLine();
        this.EndSelected();
    }

    SelectedPrevLine() {
        this.BeginSelected();
        this.GoToPrevLine();
        this.EndSelected();
    }

    SelectedLineTail() {
        this.BeginSelected();
        this.GoToLineTail();
        this.EndSelected();
    }

    SelectedLineHead() {
        this.BeginSelected();
        this.GoToLineHead();
        this.EndSelected();
    }

    SelectedTail() {
        this.BeginSelected();
        this.GoToTail();
        this.EndSelected();
    }

    SelectedHead() {
        this.BeginSelected();
        this.GoToHead();
        this.EndSelected();
    }

    SelectedAll() {
        this.m_selected_start_index = 0;
        this.m_selected_end_index = this.m_text.length;
        this.SetPositionChanged(true);
    }

    PushUndoAction(action) {
        if (this.m_is_undo_action) return;

        this.m_undo.push(action);
        if (!this.m_is_redo_action) this.m_redo = [];

        if (this.m_undo.length > MAX_UNDO_REDO_ACTION_SIZE * 2) {
            this.m_undo = this.m_undo.slice(this.m_undo.length - MAX_UNDO_REDO_ACTION_SIZE);
        }
    }

    PopUndoAction() {
        if (this.m_undo.length == 0) return;
        this.m_is_undo_action = true;
        const action = this.m_undo[this.m_undo.length - 1];
        this.PushRedoAction(action);
        if (action.m_action == ACTION_INSERT) {
            this.m_selected_start_index = action.m_start_index;
            this.m_selected_end_index = action.m_end_index;
            this.DeleteSelected();
        }
        else {
            this.m_cursor_index = action.m_start_index;
            this.InsertText(action.m_text);
        }
        this.m_undo.pop();
        this.m_is_undo_action = false;
    }

    PushRedoAction(action) {
        this.m_redo.push(action);
    }

    PopRedoAction() {
        if (this.m_redo.length == 0) return;
        this.m_is_redo_action = true;
        const action = this.m_redo[this.m_redo.length - 1];
        if (action.m_action == ACTION_INSERT) {
            this.m_cursor_index = action.m_start_index;
            this.InsertText(action.m_text);
        }
        else {
            this.m_selected_start_index = action.m_start_index;
            this.m_selected_end_index = action.m_end_index;
            this.DeleteSelected();
        }
        this.m_redo.pop();
        this.m_is_redo_action = false;
    }
}

export class Input extends IInput {
    constructor() {
        super();
        this.SetTagName("Input");
        this.SetStyleWidth(this.GetThemeInlineWidth());
        this.SetStyleHeight(this.GetThemeInlineHeight());
    }

    OnKeyEnter() {
        this.GetWindow().SetFocusElement(null);
        this.OnBlur();
    }
}

export class TextArea extends IInput {
    constructor() {
        super();
        this.SetTagName("TextArea");
        this.SetStyleWidth(-1);
        this.SetStyleHeight(-1);
        this.m_line_size = 2;

        this.SetAttributeSetterGetter("rows", (value) => this.m_line_size = parseInt(value));
    }

    GetLayoutHeight() {
        const local_height = this.GetLocalHeight();
        if (local_height > 0) return local_height;
        // 元素样式宽高被设置, 则返回元素样式宽高
        const style_height = this.GetStyleHeight();
        if (style_height >= 0) return style_height;
        // 获取行高
        const line_height = this.GetLineHeight() * this.GetFontSize();
        return this.m_line_size * (line_height < 0 ? 0 : line_height) + this.GetPaddingBorderHeight();
    }
}
